package com.cloudeasy.server.config;

import com.cloudeasy.api.util.BPwdEncoderUtil;
import com.cloudeasy.core.authorize.AuthorizeConfigManager;
import com.cloudeasy.core.properties.OAuth2ClientProperties;
import com.cloudeasy.core.properties.SecurityProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;

import java.util.List;

/**
 * OAuth2配置
 */
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
    /**
     * {@link SecurityProperties}安全属性
     */
    private final SecurityProperties securityProperties;
    /**
     * {@link TokenStore}令牌存储
     */
    private final TokenStore tokenStore;
    /**
     * 注册的{@link TokenEnhancer}列表
     */
    private final List<TokenEnhancer> tokenEnhancers;
    /**
     * {@link AuthorizeConfigManager}授权管理器
     */
    private final AuthenticationManager authenticationManager;

    public OAuth2Config(SecurityProperties securityProperties, TokenStore tokenStore, List<TokenEnhancer> tokenEnhancers, AuthenticationManager authenticationManager) {
        this.securityProperties = securityProperties;
        this.tokenStore = tokenStore;
        this.tokenEnhancers = tokenEnhancers;
        this.authenticationManager = authenticationManager;
    }

    /**
     * 客户详细信息服务配置
     *
     * @param clients {@link ClientDetailsServiceConfigurer}
     * @throws Exception 异常
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 将客户端的信息存储在内存中
        OAuth2ClientProperties client = securityProperties.getOauth2().getClient();
        clients.inMemory()
                // 配置一个客户端
                .withClient(client.getClientId())
                .secret(BPwdEncoderUtil.encode(client.getClientSecret()))
                // 配置客户端的域
                .scopes(client.getScope())
                // 配置验证类型为refresh_token和password
                .authorizedGrantTypes("refresh_token", "password")
                // 配置token的过期时间
                .accessTokenValiditySeconds(client.getAccessTokenValiditySeconds());
    }

    /**
     * 授权服务器端点配置
     * 配置token的存储方式为{@link JwtConfig}中提供的
     * {@link org.springframework.security.oauth2.provider.token.store.JwtTokenStore}
     * 配置默认的{@link AuthorizeConfigManager}
     *
     * @param endpoints {@link ClientDetailsServiceConfigurer}
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        // 配置token的存储方式为JwtTokenStore
        endpoints.tokenStore(tokenStore)
                // 配置安全认证管理
                .authenticationManager(authenticationManager);
        if (tokenEnhancers != null) {
            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
            endpoints.tokenEnhancer(tokenEnhancerChain);
        }
    }

    /**
     * 授权服务器安全配置
     * 允许所有令牌访问
     * 标记所有令牌访问为已认证
     *
     * @param security {@link AuthorizationServerSecurityConfigurer}
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()");
    }
}
